<template>
  <div class="bs-table" :class="{ 'border-none': !border }" v-loading="computedLoading" element-loading-text="加载中...">
    <!--表格区域-->
    <el-table ref="tableRef" :tooltip-options="computedTooltipOptions" border :data="computedData" :stripe="stripe" v-bind="$attrs">
      <bs-table-column v-for="(item, index) in computedColumns" :key="index" :column="item"></bs-table-column>
      <template #append>
        <slot name="append"></slot>
      </template>
      <template v-if="!computedLoading" #empty>
        <slot name="empty">
          <el-empty description="暂无数据" />
        </slot>
      </template>
    </el-table>
    <!--虚拟触发的tooltip-->
    <el-tooltip :virtual-ref="tooltip.virtualRef" virtual-triggering :visible="tooltip.visible" :placement="tooltip.placement">
      <template #content>{{tooltip.text}}</template>
    </el-tooltip>
    <!--分页区域-->
    <div class="page-area" v-if="computedShowPager">
      <!--全部条数-->
      <div class="total-num">共{{pager.total}}条数据</div>
      <el-pagination
        background
        :layout="computedPageLayout"
        v-bind="pager"
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
      />
    </div>
  </div>
</template>
<script setup lang="ts">
import BsTableColumn from './BsTableColumn.vue'
import {computed, onMounted, reactive, ref, watch, provide} from 'vue'
import Sortable from 'sortablejs'

const emit = defineEmits(['cell-drag-end'])

type BsTableProps = {
  dragable?: boolean
  /**拖拽句柄样式名 */
  dragHandleClass?: string
  loading?: boolean
  stripe?: boolean,
  columns: ColumnItem[]
  border?: boolean
  data?: any[]
  pageLayout?: string
  tooltipOptions?: any
  /**是否显示分页 */
  showPage?: boolean
  showPageByPageSize?: boolean 
  pagerOptions?: {
    pageSize: number
    pageSizes: number[]
  }
  getData?: (opts:any) => Promise<{total: number}>
}

const props = withDefaults(defineProps<BsTableProps>(), {
  columns: () => [],
  border: false,
  stripe: true,
  showPage: true
})

const tooltip = reactive({
  visible: false,
  text: '',
  virtualRef: undefined,
  placement: 'left'
})

/**注册用于表格项校验缓存 */
const validates = reactive<Record<string, any>>({})
const clearValidates = reactive<Record<string, any>>({})
const valdateErrCache = reactive<Record<string, string>>({})
provide('validates', validates)
provide('clearValidates', clearValidates)
provide('valdateErrCache', valdateErrCache)

const allColumns = ref<ColumnItem[]>(props.columns)
/**
 * 初始化所有列配置
 *
 */
const initAllColumns = () => {
  const arr:ColumnItem[] = []
  props.columns.forEach((column, index) => {
    arr.push({
      _checked: true,
      _disabled: false,
      ...column,
      __index: String(index + column.prop + (column.label || ''))
    })
  })
  allColumns.value = arr
}

initAllColumns()

watch(() => props.columns, () => initAllColumns())

const getAllColumns = () => {
  return allColumns.value
}

const computedTooltipOptions = computed(() => {
  return { enterable: true, placement: 'top', showArrow: true, hideAfter: 200, popperOptions: { strategy: 'fixed' }, popperClass: 'bs-table-tooltip', ...(props.tooltipOptions || {})}
})

const computedPageLayout = computed(() => {
  if (props.pageLayout) {
    return props.pageLayout
  }
  return 'sizes, prev, pager, next, jumper'
})

const tableRef = ref()
const isLoading = ref(false)

const sort = reactive({})

const pager = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
  pageSizes: [10, 20, 50, 100]
})
const initPager = () => {
  if (props.pagerOptions) {
    pager.pageSize = props.pagerOptions.pageSize
    pager.pageSizes = props.pagerOptions.pageSizes
  }
}
initPager()


/**
 * 设置表格拖拽排序
 */
const setSortable = () => {
  const el = tableRef.value.$el.querySelector('tbody')
  new Sortable(el, {
    animation: 180,
    delay: 0,
    handle: props.dragHandleClass? `.${props.dragHandleClass}` : '',
    onEnd: (e:any) => {
      const targetRow = computedData.value.splice(e.oldIndex, 1)
      computedData.value.splice(e.newIndex, 0, targetRow[0])
      emit('cell-drag-end')
    }
  })
}

watch(() => props.pagerOptions, () => {
  initPager()
})
const computedLoading = computed(() => {
  return isLoading.value || props.loading
})
const computedData = computed(() => {
  if (props.data) {
    props.data.forEach((el, index) => {
      if (!el._XID) {
        el._XID = 'tr' + new Date().getTime() + '-' + index
      }
    })
  }
  return props.data || []
})

const computedShowPager = computed(() => {
  if (props.showPageByPageSize && pager.total > pager.pageSize) {
    return true
  }
  if (!props.showPageByPageSize && props.showPage) {
    return true
  }
  return false
})
const computedColumns = computed(() => {
  const arr:ColumnItem[] = []
  allColumns.value.forEach(column => {
    if (column._checked) {
      arr.push({...column})
    }
  })
  return arr
})

const handleSizeChange = (size:number) => {
  pager.pageSize = size
  pager.currentPage = 1
  getTableData()
}

const handleCurrentChange = (page:number) => {
  pager.currentPage = page
  getTableData()
}
/**
 * 获取表格数据
 */
const getTableData = async () => {
  if (props.getData) {
    try {
      isLoading.value = true
      const data = await props.getData({
        currentPage: pager.currentPage,
        pageSize: pager.pageSize,
        limit: pager.pageSize,
        start: (Number(pager.currentPage) - 1) * pager.pageSize,
        ...sort
      })
      pager.total = data.total || 0
    } catch (e) {
      return Promise.reject(e)
    } finally {
      isLoading.value = false
    }
  }
  return 1
}

type QueryDataParam = {
  /**页码 */
  page: number
}

/**
 * 表格查询方法
 */
const queryData = async (param?: QueryDataParam) => {
  pager.currentPage = 1
  if (param && param.page) {
    pager.currentPage = param.page
  }
  await getTableData()
  return 1
}


type RefreshParam = {
  /**删除数量 */
  delCount: number
}

/**
 * 刷新数据
 */
const refresh = async (param?: RefreshParam) => {
  if (param) {
    if (param.delCount && pager.currentPage !== 1) {
      if ((pager.total - pager.pageSize * pager.currentPage <= 0) && computedData.value.length - param.delCount <= 0) {
        pager.currentPage--
      }
    }
  }
  await getTableData()
  return 1
}

/**
 * 获取表格数据
 */
const getTableDataValue = () => {
  return props.data
}

/**注册provide */

provide('setTooltipCfg', (cfgs: any) => {
  tooltip.visible = cfgs.visible
  tooltip.text = cfgs.text || ''
  tooltip.virtualRef = cfgs.virtualRef || undefined
  tooltip.placement = cfgs.placement || 'left'
})

/**校验相关方法 */
/**
 * 校验表格
 */
const validate = async () => {
  const err:string[] = []
  for (let key in validates) {
    const res = await validates[key]()
    if (res) {
      err.push(res)
    }
  }
  if (err.length) {
    return Promise.reject(true)
  } else {
    return Promise.resolve(false)
  }
}
/**
 * 校验表格每一行
 * @param row
 */
const validateRow = async (row:any) => {
  const err:string[] = []
  for (let key in validates) {
    if (key.indexOf(row._XID) > -1) {
      let res = await validates[key]()
      if (res) {
        err.push(res)
      }
    }
  }
  if (err.length) {
    return Promise.reject(true)
  } else {
    return Promise.resolve(false)
  }
}

/**
 * 校验表格单个单元格
 * @param row
 * @param prop 表格项prop
 */
const validateItem = async (row:any, prop:string) => {
  const err = await validates[row._XID + '&#' + prop]
  if (err) {
    return Promise.reject(true)
  } else {
    return Promise.resolve(false)
  }
}

/**
 * 清除表格校验
 */
const clearValidate = () => {
  for (let key in clearValidates) {
    clearValidates[key]()
  }
}
/**
 * 清除表格某一行校验
 */
const clearValidateRow = (row:any) => {
  for (let key in clearValidates) {
    if (key.indexOf(row._XID) > -1) {
      clearValidates[key]()
    }
  }
}

/**
 * 清除表格某一单元校验
 */
const clearValidateItem = (row:any, prop:string) => {
  const clear = clearValidates[row._XID + '&#' + prop]
  if (clear) {
    clear()
  }
}

const expose:any = {
  columns: props.columns,
  getAllColumns,
  getTableDataValue,
  queryData,
  refresh,
  validate,
  validateItem,
  validateRow,
  clearValidate,
  clearValidateItem,
  clearValidateRow
}

onMounted(() => {
  if (props.dragable) {
    setSortable()
  }
  const elTableMethods = ['clearSelection', 'getSelectionRows', 'toggleRowSelection', 'toggleAllSelection', 'toggleRowExpansion', 'setCurrentRow', 'clearSort', 'clearFilter', 'doLayout', 'sort', 'scrollTo', 'setScrollTop', 'setScrollLeft']
  elTableMethods.forEach(el => {
    expose[el] = tableRef.value[el]
  })
})

/*复制表格内方法*/

defineExpose(expose)

</script>
<style lang="scss" scoped>
.bs-table {
  :deep(.el-table) {
    --el-table-header-bg-color: #f7f8fa;
    --el-table-header-text-color: #595959;
    thead th {
      font-weight: 400;
    }
    .cell {
      padding: 0 5px;
      line-height: 31px;
    }
    .el-table__expand-icon {
      height: 28px;
    }
  }
  &.border-none {
    :deep(.el-table--border) {
      .el-table__cell {
        border-right: none;
      }
      .el-table__border-left-patch {
        width: 0;
      }
      &::before {
        width: 0;
      }
      &::after {
        width: 0;
      }
      th.el-table__cell {
        .cell {
          border-right: solid 1px var(--el-table-border-color);
        }
        &:last-child {
          .cell {
            border-right: 0;
          }
        }
      }
    }
  }
  :deep(.el-table__empty-text) {
    line-height: inherit;
    height: 100%;
    .el-empty {
      height: 100%;
      .el-empty__image {
        max-height: 100%;
      }
    }
  }
  :deep(.el-pagination) {
    &.is-background .el-pager li {
      &:hover {
        color: var(--primary-color);
      }
    }
    &.is-background .el-pager li.is-active {
      background-color: transparent;
      color: var(--primary-color);
      border: solid 1px var(--primary-color);
      border-radius: 4px;
      font-weight: 400;
    }
  }
  .page-area {
    margin-top: 16px;
    display: flex;
    .total-num {
      flex: 1;
      font-size: 14px;
      color: #595959;
      margin-right: 15px;
      display: flex;
      align-items: center;
    }
  }
}

</style>